package com.ruoyi.common.core.utils.exptimpt;

import cn.hutool.core.collection.CollectionUtil;
import com.ruoyi.common.core.annotation.Excel;
import com.ruoyi.common.core.constant.CommonConstants;
import com.ruoyi.common.core.domain.BaseExcel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StopWatch;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author huangliang
 * @date 2024-09-04 17:16
 * @description 导入导出抽象策略类
 */
@Component
@Slf4j
public class ExImportContext<T extends BaseExcel> {

    //1.导入
    //    模板校验
    //    解析excel
    //    数据校验(责任链)
    //    返回异常数据excel
    //2.导出
    //    处理关联数据id/字典编码项 -> 转为或增加对应描述字段
    //3.模板下载
    //    补充用到的字典项, sheet页

    @Lazy
    @Resource
    private List<AbstractExImportStrategy<T>> importStrategyList;

    /**
     * 获取对应的导入策略
     * @param exImportEnum 枚举
     */
    private AbstractExImportStrategy<T> getExImportStrategy(IExImportEnum exImportEnum) {
        //获取对应的导入策略
        AbstractExImportStrategy<T> importStrategy = importStrategyList.stream()
                .filter(t -> t.getExImportEnum().getModuleCode().equals(exImportEnum.getModuleCode())).findFirst().orElse(null);
        Assert.notNull(importStrategy, "未找到对应的导入策略");
        return importStrategy;
    }

    /**
     * 导入数据
     * @param exImportEnum 导入
     * @param file 文件
     */
    public Integer importData(IExImportEnum exImportEnum, HttpServletResponse response, MultipartFile file) throws IOException {
        log.info("导入({})数据-开始: ", exImportEnum.getModuleName());
        StopWatch stopWatch = new StopWatch();
        //获取对应的导入策略
        AbstractExImportStrategy<T> importStrategy = getExImportStrategy(exImportEnum);
        stopWatch.start("解析");
        //解析excel文件
        importStrategy.setDataList(importStrategy.getUtil().importExcel(file.getInputStream()));
        Assert.notEmpty(importStrategy.getDataList(), "解析excel文件无数据");
        stopWatch.stop();
        stopWatch.start("校验");
        //数据校验, 如果没有校验器则跳过数据校验
        if (importStrategy.getImportHandlerChain() != null){
            //拷贝一份, 防止修改原数据
            List<T> originalDataList = importStrategy.getDataList().stream()
                    .map(SerializationUtils::clone).collect(Collectors.toList());
            //数据校验
            importStrategy.getImportHandlerChain().handle(importStrategy.getDataList(), importStrategy);
            //获取错误数据条数
            long count = importStrategy.getDataList().stream().filter(t -> CollectionUtil.isNotEmpty(t.getErrorList())).count();
            if (count > 0) {
                log.info("导入数据错误条数: {}", count);
                importStrategy.getDataList().stream()
                        .filter(t -> CollectionUtil.isNotEmpty(t.getErrorList()))
                        .forEach(t -> t.setErrorMsg(String.join(",", t.getErrorList())));
                //将importStrategy.getDataList()中的错误信息覆盖到原数据中
                for (int i = 0; i < originalDataList.size(); i++) {
                    originalDataList.get(i).setErrorMsg(importStrategy.getDataList().get(i).getErrorMsg());
                }
                importStrategy.getUtil().exportErrorExcel(response, originalDataList, "错误信息");
                return -1;
            }
        }
        stopWatch.stop();
        stopWatch.start("入库");
        //数据入库
        Integer importedCount = importStrategy.insertData();
        log.info("导入({})数据-结束, 导入成功条数{}", exImportEnum.getModuleName(), importedCount);
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
        return importedCount;
    }

    /**
     * 导出数据
     * @param exImportEnum 枚举
     * @param response 响应
     */
    public void exportData(IExImportEnum exImportEnum, HttpServletResponse response, List<T> dataList) {
        AbstractExImportStrategy<T> importStrategy = getExImportStrategy(exImportEnum);
        //处理导出数据
        if (importStrategy.getExportHandlerChain() != null && CollectionUtil.isNotEmpty(dataList)){
            importStrategy.getExportHandlerChain().handle(dataList, importStrategy);
        }
        importStrategy.getUtil().exportExcel(response, dataList, exImportEnum.getModuleName() + "数据");
    }


    /**
     * 下载模板
     *
     * @param exImportEnum         枚举
     * @param response                     响应
     */
    public void downImportTemplate(IExImportEnum exImportEnum, HttpServletResponse response) {
        //获取对应的导入策略
        AbstractExImportStrategy<T> importStrategy = getExImportStrategy(exImportEnum);
        response.setContentType(CommonConstants.CONTENT_TYPE_SHEET);
        response.setCharacterEncoding(CommonConstants.UTF8);
        importStrategy.getUtil().init(new ArrayList<>(), exImportEnum.getModuleName() + "数据",  StringUtils.EMPTY, Excel.Type.EXPORT);
        importStrategy.getUtil().exportExcel(response);
//        importStrategy.getUtil().exportExcel(response, new ArrayList<>(), exImportEnum.getModuleName() + "数据");
    }

}
